﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace FastLimitFlow
{
    public class TokenBucket : IRateLimit
    {
        //令牌队列
        public ConcurrentQueue<object> TokenQueue { set; get; }
        //最大令牌数
        public int MaxTokenSize { set; get; }
        //令牌产生速率 个/秒
        public int TokenProduceRate { set; get; }
        private CancellationTokenSource cancelToken;
        private Task task;

        public TokenBucket(int MaxTokenSize, int TokenProduceRate)
        {
            cancelToken = new CancellationTokenSource();
            TokenQueue = new ConcurrentQueue<object>();
            this.MaxTokenSize = MaxTokenSize;
            this.TokenProduceRate = TokenProduceRate;
            for (int i = 0; i < MaxTokenSize; i++)
            {
                TokenQueue.Enqueue(new object());
            }
            task = Task.Factory.StartNew(new Action(TokenProcess), cancelToken.Token);
            //初始为满桶
            //令牌的限流原理为
            //根据一定加入令牌
            //如果固定增加为2个/秒，则增加一个休眠0.5秒
            //可用条件为，队列未空，则能取到令牌，如果没有使用，则桶增至最大值，如果桶空，则取出率与使用率保持一致，达到限流，同时能满足桶最大值的突发流量
        }

        public void TokenProcess()
        {
            //每生成一个令牌等待多久,比如速率为2个/秒，则生产一个需要等待0.5秒
            int sleep = 1000 / TokenProduceRate;
            DateTime start = DateTime.Now;
            while (cancelToken.Token.IsCancellationRequested == false)
            {
                try
                {
                    //如果未超出最大令牌书，入队列
                    if (TokenQueue.Count < MaxTokenSize)
                    {
                        TokenQueue.Enqueue(new object());
                    }
                }
                catch (Exception)
                {

                    throw;
                }
                finally
                {
                    Thread.Sleep(sleep);
                }
            }
        }

        public void Dispose()
        {
            cancelToken.Cancel();
        }

        //是否有令牌可用
        public bool IsHaveToken()
        {
            if (TokenQueue.IsEmpty)
            {
                return false;
            }
            else
            {
                object result = null;
                TokenQueue.TryDequeue(out result);
                if (null != result)
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }

        }

    }
}
